home *** CD-ROM | disk | FTP | other *** search
- /* SBIO.C */
- /* Soundblaster 16 basic audio I/O functions */
- /* Copyright 1995 by Ethan Brodsky. All rights reserved */
- /* Modified extensively by Philip VanBaren to suit my purposes */
- /* Modified by Emil Laurentiu Wednesday, 06 August 1997 */
-
- /* Interface variables that can be changed in the background */
- volatile int sb16dmarunning;
- volatile char curblock;
-
- #include <alloc.h>
- #include <stdio.h>
- #include <dos.h>
- #include <mem.h>
- #include <stdlib.h>
- #include "sbio.h"
- #include "freq.h" /* Needed for DOUT() definition only */
-
- #define lo(value) (unsigned char)((value) & 0x00FF)
- #define hi(value) (unsigned char)((value) >> 8)
-
- int mixerport;
- int mixdataport;
- int resetport;
- int readport;
- int writeport;
- int pollport;
- int poll16port;
-
- int pic_rotateport;
- int pic_maskport;
-
- int dma_maskport;
- int dma_clrptrport;
- int dma_modeport;
- int dma_baseaddrport;
- int dma_countport;
- int dma_pageport;
-
- char irq_startmask;
- char irq_stopmask;
- char irq_intvector;
- char int_controller;
-
- char dma_startmask;
- char dma_stopmask;
- char dma_mode;
-
- /* This function is defined in sc_sb16.c */
- extern void interrupt sb16_callback( void );
- void
- interrupt( *oldintvector ) ( ) = NULL;
- int handlerinstalled;
-
- void far *dmabuffer = NULL; /* Twice the size of the output buffer */
- int far *dmaptr = NULL; /* Pointer to the used portion */
-
- unsigned long buf_addr; /* 16-bit addressing */
- unsigned char buf_page;
- unsigned int buf_ofs;
-
- mode iomode; /* Flags input or output mode */
-
- /* Low level sound card I/O */
- void
- write_dsp( unsigned char value )
- {
- while ( inp( writeport ) & 0x80 ); /* Wait for bit 7 to be cleared */
- outp( writeport, value );
- }
-
- unsigned char
- read_dsp( void )
- {
- long timeout = 1000000L;
- /* Wait for bit 7 to be set, or for a timeout */
- while ( ( !( inp( pollport ) & 0x80 ) ) && ( --timeout ) );
- return inp( readport );
- }
-
- int
- reset_dsp( void )
- {
- int i;
-
- sb16dmarunning = 0;
- outp( resetport, 1 );
- outp( resetport, 0 );
- i = 100;
-
- while ( ( read_dsp( ) != 0xAA ) && i-- );
- return i;
- }
-
- /* Convert a far pointer to a linear address, for DMA addressing */
- #define getlinearaddr(p) ((unsigned long)FP_SEG(p)*16 + (unsigned long)FP_OFF(p))
-
- /* Initialization and shutdown */
- void
- installhandler( void )
- {
- /* Install the interrupt handler */
- disable( ); /* Disable interrupts */
- outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ */
- oldintvector = getvect( irq_intvector ); /* Save old vector */
- setvect( irq_intvector, sb16_callback ); /* Install new handler */
- outp( pic_maskport, ( inp( pic_maskport ) & irq_startmask ) ); /* Unmask IRQ */
- enable( ); /* Reenable interupts */
- handlerinstalled = 1;
- }
- void
- uninstallhandler( void )
- {
- sb16dmarunning = 0;
- disable( ); /* Disable interrupts */
- outp( pic_maskport, ( inp( pic_maskport ) | irq_stopmask ) ); /* Mask IRQ */
- setvect( irq_intvector, oldintvector ); /* Restore old vector */
- enable( ); /* Enable interrupts */
- handlerinstalled = 0;
- }
-
- /* This function is run at program exit to ensure cleanup */
- void
- sb_exitproc( void )
- {
- sb16dmarunning = 0;
- outp( 0x20, 0x20 );
- outp( 0xA0, 0x20 ); /* Acknowledge any hanging ints */
- write_dsp( 0xD5 ); /* Stop digitized sound xfer */
- outp( dma_maskport, dma_stopmask ); /* Mask DMA channel */
- if ( handlerinstalled )
- uninstallhandler( ); /* Uninstall int handler */
- reset_dsp( ); /* Reset SB DSP */
- }
-
- /*
- * Initialize the Soundblaster-16 card with the settings:
- * baseio: base IO address for the card
- * irq: IRQ channel used by the card
- * dma16: 16-bit DMA channel used by the card
- * io: sampling mode, either "input" or "output"
- * length: maximum block size to be used, in samples
- * (this is used for allocating the DMA buffer)
- */
- int
- init_sb( int baseio, char irq, char dma16, mode io, unsigned int length )
- {
- int val, onboard;
-
- /* Sound card IO ports */
- mixerport = baseio + 0x004;
- mixdataport = baseio + 0x005;
- resetport = baseio + 0x006;
- readport = baseio + 0x00A;
- writeport = baseio + 0x00C;
- pollport = baseio + 0x00E;
- poll16port = baseio + 0x00F;
-
- /* Reset DSP */
- sb16dmarunning = 0;
- if ( !reset_dsp( ) )
- return 0;
-
- /* Verify that we have a SB-16 at this address */
- write_dsp( 0xE1 );
- val = read_dsp( ); /* Get the major version number */
- read_dsp( ); /* Grab the minor version number also */
- if ( val < 4 )
- return 0;
-
- /*
- * Check the current IRQ and DMA settings, and compare with the values
- * passed to this routine
- */
- outportb( mixerport, 0x80 ); /* IRQ settings */
- val = inportb( mixdataport );
- onboard = -1;
- if ( val & 0x01 )
- {
- onboard = 2;
- DOUT( "SB16: IRQ2 enabled" );
- }
- if ( val & 0x02 )
- {
- onboard = 5;
- DOUT( "SB16: IRQ5 enabled" );
- }
- if ( val & 0x04 )
- {
- onboard = 7;
- DOUT( "SB16: IRQ7 enabled" );
- }
- if ( val & 0x08 )
- {
- onboard = 10;
- DOUT( "SB16: IRQ10 enabled" );
- }
- if ( onboard == -1 )
- {
- puts( "Error: Soundblaster has no IRQ enabled, aborting ..." );
- return ( 0 );
- }
- switch ( irq )
- {
- case 2:
- if ( !( val & 0x01 ) )
- {
- irq = -1;
- } break;
- case 5:
- if ( !( val & 0x02 ) )
- {
- irq = -1;
- } break;
- case 7:
- if ( !( val & 0x04 ) )
- {
- irq = -1;
- } break;
- case 10:
- if ( !( val & 0x08 ) )
- {
- irq = -1;
- } break;
- default:
- irq = -1;
- }
- if ( irq == -1 )
- {
- printf( "Warning: Soundblaster has IRQ%d selected, using that value instead\n", onboard );
- irq = onboard;
- }
- outportb( mixerport, 0x81 ); /* DMA settings */
- val = inportb( mixdataport );
- onboard = -1;
- if ( val & 0x01 )
- { /* onboard=0; */
- DOUT( "SB16: DMA0 enabled" );
- }
- if ( val & 0x02 )
- { /* onboard=1; */
- DOUT( "SB16: DMA1 enabled" );
- }
- if ( val & 0x08 )
- { /* onboard=3; */
- DOUT( "SB16: DMA3 enabled" );
- }
- if ( val & 0x20 )
- {
- onboard = 5;
- DOUT( "SB16: DMA5 enabled" );
- }
- if ( val & 0x40 )
- {
- onboard = 6;
- DOUT( "SB16: DMA6 enabled" );
- }
- if ( val & 0x80 )
- {
- onboard = 7;
- DOUT( "SB16: DMA7 enabled" );
- }
- if ( onboard == -1 )
- {
- puts( "Error: Soundblaster has no 16-bit DMA enabled, aborting ..." );
- return ( 0 );
- }
- switch ( dma16 )
- {
- // case 0: if(!(val&0x01)) { dma16=-1; } break;
- // case 1: if(!(val&0x02)) { dma16=-1; } break;
- // case 3: if(!(val&0x08)) { dma16=-1; } break;
- case 5:
- if ( !( val & 0x20 ) )
- {
- dma16 = -1;
- } break;
- case 6:
- if ( !( val & 0x40 ) )
- {
- dma16 = -1;
- } break;
- case 7:
- if ( !( val & 0x80 ) )
- {
- dma16 = -1;
- } break;
- default:
- dma16 = -1;
- }
- if ( dma16 == -1 )
- {
- printf( "Warning: Soundblaster has DMA%d selected, using that value instead\n", onboard );
- dma16 = onboard;
- }
-
- #ifdef DEBUG_OUTPUT
- /* Check if the IRQs are enabled */
- outportb( mixerport, 0x82 ); /* IRQ status */
- val = inportb( mixdataport );
- if ( val & 0x01 )
- {
- DOUT( "SB16: 8-bit IRQ active" );
- }
- if ( val & 0x02 )
- {
- DOUT( "SB16: 16-bit IRQ active" );
- }
- if ( val & 0x04 )
- {
- DOUT( "SB16: MPU-401 IRQ active" );
- }
- #endif
- /* These two lines are strictly a kludge for freq.exe */
- outportb( mixerport, 0x3d ); /* Input control */
- outportb( mixdataport, 0x7f );/* Use all channels for input */
-
- /* Compute interrupt ports and parameters */
- if ( irq < 8 )
- {
- int_controller = 1;
- pic_rotateport = 0x20;
- pic_maskport = 0x21;
- irq_intvector = 0x08 + irq;
- }
- else
- {
- int_controller = 2;
- pic_rotateport = 0xA0;
- pic_maskport = 0x21;
- irq_intvector = 0x70 + irq - 8;
- }
- irq_stopmask = 1 << ( irq % 8 );
- irq_startmask = ~irq_stopmask;
-
- /* Compute DMA ports and parameters */
- dma_maskport = 0xD4;
- dma_clrptrport = 0xD8;
- dma_modeport = 0xD6;
- dma_baseaddrport = 0xC0 + 4 * ( dma16 - 4 );
- dma_countport = 0xC2 + 4 * ( dma16 - 4 );
-
- switch ( dma16 )
- {
- case 5:
- dma_pageport = 0x8B;
- break;
- case 6:
- dma_pageport = 0x89;
- break;
- case 7:
- dma_pageport = 0x8A;
- break;
- }
-
- dma_stopmask = dma16 - 4 + 0x04; /* 000001xx */
- dma_startmask = dma16 - 4 + 0x00; /* 000000xx */
-
- /* Allocate a buffer for DMA transfer */
- /* (need a block of memory that does not cross a page boundary) */
- if ( ( dmabuffer = farmalloc( 8 * length ) ) == NULL )
- {
- puts( "Unable to allocate DMA buffer." );
- exit( 1 );
- }
- dmaptr = ( int far * ) dmabuffer;
- if ( ( ( getlinearaddr( dmabuffer ) >> 1 ) % 65536L ) + length * 2 > 65536L )
- dmaptr += 2 * length; /* Pick second half to avoid crossing
- * boundary */
-
- /* Compute DMA parameters */
- buf_addr = getlinearaddr( dmaptr );
- buf_page = ( unsigned int ) ( buf_addr >> 16 );
- buf_ofs = ( unsigned int ) ( ( buf_addr >> 1 ) % 65536L );
-
- /* Other initialization */
- iomode = io;
- switch ( iomode )
- {
- case input:
- dma_mode = dma16 - 4 + 0x54;
- break; /* 010101xx */
- case output:
- dma_mode = dma16 - 4 + 0x58;
- break; /* 010110xx */
- }
-
- installhandler( ); /* Install interrupt handler */
- atexit( sb_exitproc ); /* Install exit procedure */
-
- return 1;
- }
-
- /*
- * Shut down the sampling process, clean up the interrupts,
- * and free up the memory allocation
- */
- void
- shutdown_sb( void )
- {
- sb16dmarunning = 0;
- reset_dsp( );
- if ( handlerinstalled )
- uninstallhandler( );
- farfree( dmabuffer );
- }
-
- /*
- * Start continuous I/O at a rate of {rate} Hz, with a call to the
- * callback_sb16 procedure after every block of length {length} has
- * been finished. The sampling input or output is done on alternating
- * blocks pointed to by (dmaptr+curblock*length).
- * i.e. When curblock is 0, the current block is (dmaptr).
- * When curblock is 1, the current block is (dmaptr+length)
- * The variable curblock is maintained in the callback_sb16 routine.
- */
- void
- startio( unsigned int rate, unsigned long length )
- {
- sb16dmarunning = 1;
- curblock = 0;
-
- /* Program DMA controller */
- outp( dma_maskport, dma_stopmask );
- outp( dma_clrptrport, 0x00 );
- outp( dma_modeport, dma_mode );
- outp( dma_baseaddrport, lo( buf_ofs ) ); /* Low byte of offset */
- outp( dma_baseaddrport, hi( buf_ofs ) ); /* High word of offset */
- outp( dma_countport, lo( length * 2 - 1 ) ); /* Low byte of count */
- outp( dma_countport, hi( length * 2 - 1 ) ); /* High byte of count */
- outp( dma_pageport, buf_page );
- outp( dma_maskport, dma_startmask );
-
- /* Program sound card */
- switch ( iomode )
- {
- case input:
- write_dsp( 0x42 );
- break; /* Set input sampling rate */
- case output:
- write_dsp( 0x41 );
- break; /* Set output sampling rate */
- }
- write_dsp( hi( rate ) ); /* High byte of sampling rate */
- write_dsp( lo( rate ) ); /* Low byte of sampling rate */
- switch ( iomode )
- {
- case output:
- write_dsp( 0xB6 );
- break; /* 16-bit D->A, A/I, FIFO */
- case input:
- write_dsp( 0xBE );
- break; /* 16-bit A->D, A/I, FIFO */
- }
- write_dsp( 0x10 ); /* DMA Mode: 16-bit signed mono */
- write_dsp( lo( length - 1 ) );/* Low byte of block length */
- write_dsp( hi( length - 1 ) );/* High byte of block length */
- }
-
- /*
- * Stops the current sampling process.
- */
- void
- stopio( void )
- {
- outp( 0x20, 0x20 );
- outp( 0xA0, 0x20 ); /* Acknowledge any hanging ints */
- write_dsp( 0xD9 ); /* Stop digitized sound xfer */
- outp( dma_maskport, dma_stopmask ); /* Mask DMA channel */
- sb16dmarunning = 0;
- reset_dsp( ); /* Reset SB DSP */
- }
-
- /* Mixer setting and reading functions */
- void
- set_cd_level( unsigned int level )
- {
- level = level * 256 / 100;
- if ( level > 255 )
- level = 255;
- outportb( mixerport, 0x36 ); /* CD Audio left */
- outportb( mixdataport, level );
- outportb( mixerport, 0x37 ); /* CD Audio right */
- outportb( mixdataport, level );
- }
-
- void
- set_mic_level( unsigned int level )
- {
- level = level * 256 / 100;
- if ( level > 255 )
- level = 255;
- outportb( mixerport, 0x3A ); /* Microphone */
- outportb( mixdataport, level );
- }
-
- void
- set_line_level( unsigned int level )
- {
- level = level * 256 / 100;
- if ( level > 255 )
- level = 255;
- outportb( mixerport, 0x38 ); /* Line In left */
- outportb( mixdataport, level );
- outportb( mixerport, 0x39 ); /* Line In right */
- outportb( mixdataport, level );
- }
-
- unsigned int
- get_cd_level( void )
- {
- unsigned int level;
- outportb( mixerport, 0x36 ); /* CD Audio left */
- level = inportb( mixdataport );
- outportb( mixerport, 0x37 ); /* CD Audio right */
- level += inportb( mixdataport );
- level = level * 50 / 256;
- if ( level > 100 )
- level = 100;
- return ( level );
- }
-
- unsigned int
- get_mic_level( void )
- {
- unsigned int level;
- outportb( mixerport, 0x3A ); /* Microphone */
- level = inportb( mixdataport );
- level = level * 100 / 256;
- if ( level > 100 )
- level = 100;
- return ( level );
- }
-
- unsigned int
- get_line_level( void )
- {
- unsigned int level;
- outportb( mixerport, 0x38 ); /* Line In left */
- level = inportb( mixdataport );
- outportb( mixerport, 0x39 ); /* Line In right */
- level += inportb( mixdataport );
- level = level * 50 / 256;
- if ( level > 100 )
- level = 100;
- return ( level );
- }
-
- void
- set_master_level( int level )
- {
- level &= 0xf;
- level |= level << 4;
- outportb( sb_addr + 0x4, 0x22 ); /* Master volume */
- outportb( sb_addr + 0x5, level );
- return;
- }
-
- void
- set_fm_level( int level )
- {
- level &= 0xf;
- level |= level << 4;
- outportb( sb_addr + 0x4, 0x26 ); /* FM volume */
- outportb( sb_addr + 0x5, level );
- return;
- }
-
-